#pragma once

#include "../comm/util.hpp"
#include "../comm/httplib.h"
#include "../comm/log.hpp"
#include "oj_model.hpp"
#include <iostream>
#include <string>
#include <vector>
#include "oj_view.hpp"
#include <mutex>
#include <jsoncpp/json/json.h>
#include <algorithm>



namespace ns_control
{
    using namespace ns_model;
    using namespace std;
    using namespace ns_util;
    using namespace httplib; 
    using namespace ns_log;
    using namespace ns_view;

    //提供服务的主机
    class Machine
    {
    public:
        std::string ip; // 主机IP地址
        int port;       // 主机端口号
        uint64_t load; // 主机负载，表示当前主机的负载情况
        std::mutex *mtx; //C++中mutex是禁止拷贝的，所以需要指针来传递互斥锁，否则在push_back时会产生临时拷贝

        Machine()
            : ip(""), port(0), load(0), mtx(nullptr)
        {

        }
        ~Machine()
        {
            
        }

        // 增加负载
        void IncLoad()
        {
            if(mtx) mtx->lock(); // 使用锁保护负载更新
            load++;
            if(mtx) mtx->unlock();
        }
        // 减少负载
        void DecLoad()
        {
            if(mtx) mtx->lock(); // 使用锁保护负载更新
            if (load > 0)
            {
                load--;
            }
            if(mtx) mtx->unlock();
        }

        // 获取负载
        uint64_t GetLoad()
        {
            uint64_t _load=0;
            if(mtx) mtx->lock(); // 使用锁保护负载读取
            _load = load;
            if(mtx) mtx->unlock();
            return _load;
        }
        void ResetLoad()
        {
            if(mtx) mtx->lock();
            load = 0;
            if(mtx) mtx->unlock();
        }
    };
    // 模板文件路径
    const std::string template_path = "./conf/service_machine.conf"; // 模板文件路径

    //负载均衡模块
    class LoadBalancer
    {
    private:
         // 存储所有可用的主机
         //每个主机都有自己的下标，充当当前主机的ID
        std::vector<Machine> machines;
        //所有在线主机的ip
        std::vector<int> online;
        //所有离线主机的ip
        std::vector<int> offline;
        // 互斥锁，保护对 machines 和 online 的访问
        std::mutex mtx; 
    public:
        LoadBalancer()
        {
            assert(LoadConf(template_path));
            LOG(INFO) << "负载均衡器初始化成功\n";
        }
        ~LoadBalancer()
        {
           
        }

        bool LoadConf(const std::string &machine_list)
        {
            std::ifstream in(machine_list);
            if (!in.is_open())
            {
                LOG(FATAL) << "加载主机列表失败: " << machine_list << "\n";
                return false;   
            }

            std::string line;
            while (std::getline(in, line))
            {
                std::vector<std::string> tokens;
                StringUtil::SplitString(line, &tokens, ":");
                if (tokens.size() != 2)
                {
                    LOG(WARNING) << "切分  " << line << "失败\n";
                    continue; // 跳过无效行
                }

                Machine m;
                m.ip = tokens[0];
                m.port = std::stoi(tokens[1]);
                m.load = 0; // 初始负载为0
                m.mtx = new std::mutex(); // 创建互斥锁

                online.push_back(machines.size()); // 将主机ID添加到在线列表
                machines.push_back(m);
            }
            return true;
        }

        bool SmartChoice(int *id,Machine** m)
        {
            //1.选择主机，更新主机负载
            //
            //
            mtx.lock();
            int online_num=online.size();
            if(online_num==0)
            {
                mtx.unlock();
                LOG(FATAL) << "没有在线主机可用\n";
                return false; // 没有在线主机
            }
            //2.选择负载最低的主机
            *id=online[0]; // 默认选择第一个在线主机
            *m = &machines[online[0]]; // 默认选择第一个在线主机
            int min_load = machines[online[0]].GetLoad();
            for(int i = 1; i < online_num; ++i)
            {
                uint64_t curr_load = machines[online[i]].GetLoad();
                if (curr_load < min_load)
                {
                    min_load = curr_load;
                    *id = online[i]; // 更新主机ID
                    *m = &machines[online[i]]; // 更新主机指针
                }
            }
            mtx.unlock();
            return true;
        }

        void OfflineMachine(int which)
        {
            // 将指定主机标记为离线
            mtx.lock();
            for(auto iter = online.begin(); iter != online.end(); ++iter)
            {
                if(*iter == which)
                {
                    machines[which].ResetLoad();
                    online.erase(iter); // 从在线列表中移除
                    offline.push_back(which); // 将主机ID添加到离线列表
                    break;
                }
            }
            mtx.unlock();
        }

        void OnlineMachine()
        {
            // 将所有离线主机重新标记为在线
            mtx.lock();
            online.insert(online.end(), offline.begin(), offline.end());
            offline.erase(offline.begin(), offline.end());
            mtx.unlock();

            LOG(INFO) << "所有的主机有上线啦!" << "\n";
       }
        void ShowMachines()
        {
            // 显示所有主机信息
            mtx.lock();
            std::cout<<"当前在线主机列表:\n";
            for (const auto &m : online)
            {
               std::cout<< m<<" ";
            }

            std::cout<<"\n";
            std::cout<<"当前离线主机列表:\n";
            for (const auto &m : offline)
            {
                std::cout<< m<<" ";
            }
            std::cout<<"\n";
            mtx.unlock();
        } 
    };


    class Control
    {
    public:
        Control()
        {}
        ~Control()
        {}
        void RecoveryMachine()
        {
            // 恢复所有离线主机
            load_balancer_.OnlineMachine();
            LOG(INFO) << "所有离线主机已恢复\n";
        }
        bool AllQuestions(std::string *html)
        {
            bool ret = true;
            vector<struct Question> all;
            // 获取所有题目列表
            if (model.GetAllQuestions(&all))
            {
                sort(all.begin(), all.end(), [](const struct Question &a, const struct Question &b) {
                    return stoi(a.number.c_str()) < stoi(b.number.c_str()); // 按照题目编号排序
                });
                //获取题目成功，将所有的题目构建为网页
                view_.AllExpandHtml(all,html);
            }
            else
            {
                *html = "获取题目失败";
                ret = false;
            }

            return ret;
        }
        // 控制器的主要功能函数
        bool Question(const std::string &number, std::string *html)
        {
            struct Question q;
            bool ret = true;
            // 根据题目编号获取题目信息
            if (model.GetOneQuestion(number, &q))
            {
                //获取题目成功，将题目构建为网页
                view_.OneExpandHtml(q, html);
                ret = true;

            }
            else
            {
                *html = "获取题目失败"+number+"不存在";
                ret = false;
            }
            return ret;
        }
        void Judge(const std::string& number,const std::string in_json,std::string *out_json)
        {
            LOG(DEBUG)<<in_json<<"\nnumber: "<<number<<"\n";
            //0.根据题目编号获取题目信息
            struct Question q; 
            model.GetOneQuestion(number, &q);
            //1.对in_json进行反序列化，得到题目的id，得到用户提交的源代码
            Json::Reader reader;
            Json::Value in_value;
            reader.parse(in_json, in_value);
            std::string code = in_value["code"].asString(); // 用户提交的代码
            //2.重新拼接用户代码和测试用例代码，形成新的代码
            Json::Value compile_value;
            compile_value["input"] = in_value["input"].asString(); // 用户输入
            compile_value["code"] = code + q.tail; // 拼接代码
            compile_value["cpu_limit"] = q.cpu_limit; // 题目时间限制
            compile_value["mem_limit"] = q.mem_limit; // 题目空间限制 

            Json::FastWriter writer;
            std::string compile_string = writer.write(compile_value); // 序列化为 JSON 字符串
            //3.选择负载均衡最低的主机
            while(true)
            {
                int id=0;
                Machine* m = nullptr;
                if(!load_balancer_.SmartChoice(&id, &m))
                {
                    break;
                }
                //4.发起http请求，得到结果
                Client cli(m->ip,m->port);
                m->IncLoad(); // 增加主机负载
                LOG(INFO) << " 选择主机成功, 主机id: " << id << " 详情: " << m->ip << ":" << m->port << " 当前主机的负载是: " << m->GetLoad() << "\n";
                if(auto res = cli.Post("/compile_and_run", compile_string, "application/json;charset=utf-8"))
                {
                    //5.将结果复制给out_json
                    if(res->status == 200)
                    {
                        *out_json = res->body; // 获取响应体内容
                        m->DecLoad(); // 减少主机负载
                        LOG(INFO) << "请求编译和运行服务成功..." << "\n";
                        break;
                    }
                    m->DecLoad(); // 减少主机负载
                }
                else
                {
                    LOG(ERROR) << " 当前请求的主机id: " << id << " 详情: " << m->ip << ":" << m->port << " 可能已经离线"<< "\n";
                    load_balancer_.OfflineMachine(id); // 将主机标记为离线
                    load_balancer_.ShowMachines(); //仅仅是为了用来调试
                }
            } 
        }
    private:
        Model model; // 数据模型对象，用于数据交互
        View view_;   // 视图对象，用于生成网页内容
        LoadBalancer load_balancer_; // 负载均衡器对象
    };
} // namespace ns_control